package com.yunduansing.demo.service.impl;

import java.util.Calendar;
import java.util.concurrent.TimeUnit;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.yunduansing.demo.service.AccountService;
import com.yunduansing.demo.service.JwtService;
import com.yunduansing.demo.util.JwtTokenUtil;
import com.yunduansing.demo.exception.MyException;
import com.yunduansing.demo.filter.JwtAuthenticationToken;
import com.yunduansing.demo.model.account.LoginUserModel;
import com.yunduansing.demo.model.common.CacheKeyPrefix;
import com.yunduansing.demo.model.common.PassportModel;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.www.NonceExpiredException;
import org.springframework.stereotype.Service;

@Service
public class JwtServiceImpl implements JwtService {
    @Autowired
    private RedisTemplate redisTemplate;
    private final String CACHE_KEY_PREFIX = "token:";
    private final String CACHE_KEY_PREFIX_REFRESH = "token:refresh:";
    @Value("${login.user.expires}")
    private long LoginUserExpire;
    @Autowired
    private AccountService accountService;

    @Override
    public boolean validateToken(String username, String token, boolean isRefresh) {
        DecodedJWT jwt = JWT.decode(token);
        if (jwt.getExpiresAt().before(Calendar.getInstance().getTime()))
            throw new NonceExpiredException("Token expires");
        var salt = redisTemplate.opsForValue().get(CACHE_KEY_PREFIX + username);
        if (isRefresh) {
            salt = redisTemplate.opsForValue().get(CACHE_KEY_PREFIX_REFRESH + username);
        }
        if (salt == null) {
            throw new NonceExpiredException("Token expires");
        }
        String encryptSalt = salt.toString();// user.getPassword();
        try {
            Algorithm algorithm = Algorithm.HMAC256(encryptSalt);
            JWTVerifier verifier = JWT.require(algorithm).withSubject(username).build();
            verifier.verify(token);
            return true;
        } catch (Exception e) {
            throw new BadCredentialsException("JWT token verify fail", e);
        }
    }

    @Override
    public String getToken(String username) {
        var token = redisTemplate.opsForValue().get(CacheKeyPrefix.LOGIN_USER_TOKEN + username);
        if(token!=null) return token.toString();
        return null;
    }

    @Override
    public String getRereshToken(String username) {
        var token = redisTemplate.opsForValue().get(CacheKeyPrefix.LOGIN_USER_TOKEN+"refresh:" + username);
        if(token!=null) return token.toString();
        return null;
    }

    @Override
    public PassportModel getLoginUser() {
        if(SecurityContextHolder.getContext()!=null&&SecurityContextHolder.getContext().getAuthentication()!=null&&SecurityContextHolder.getContext().getAuthentication().isAuthenticated()){
            UserDetails user=(UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            var cache=redisTemplate.opsForValue().get(CacheKeyPrefix.LOGIN_USER+user.getUsername());
            if(cache!=null) {
                var loginUserCache= (LoginUserModel)cache;
                return new PassportModel(loginUserCache.getUserId(),user.getUsername(), loginUserCache.isAdmin(),"",user.getUsername());
            }
            var loginUser=accountService.login(user.getUsername());
            String token=redisTemplate.opsForValue().get(CacheKeyPrefix.LOGIN_USER_TOKEN+user.getUsername()).toString();
            var passport=new PassportModel(loginUser.getUserId(),user.getUsername(), loginUser.isAdmin(),token,user.getUsername());
            redisTemplate.opsForValue().set(CacheKeyPrefix.LOGIN_USER+user.getUsername(), passport,LoginUserExpire,TimeUnit.MILLISECONDS);
            return passport;
        }
        return null;
    }

    @Override
    public LoginUserModel getLoginUserBytoken(String token) throws MyException {
        var jwt=JwtTokenUtil.parseToken(token);
        var cacheUser=redisTemplate.opsForValue().get(CacheKeyPrefix.LOGIN_USER+jwt.getSubject());
        if(cacheUser!=null) {
            var user= (LoginUserModel)cacheUser;
            user.setPassword("");
            return user;
        }
        throw new MyException("登录已过期，请重新登录。");
    }
    
}
